// Messenger.cs v0.1 (20090925) by Rod Hyde (badlydrawnrod).

//
// This is a C# messenger (notification center) for Unity. It uses delegates
// and generics to provide type-checked messaging between event producers and
// event consumers, without the need for producers or consumers to be aware of
// each other.

using System;
using System.Collections.Generic;


/**
 * A messenger for events that have no parameters.
 */
static public class Messenger
{
    private static Dictionary<string, Delegate> eventTable = new Dictionary<string, Delegate>();

    static public void AddListener(string eventType, Callback handler)
    {
        // Obtain a lock on the event table to keep this thread-safe.
        lock (eventTable)
        {
            // Create an entry for this event type if it doesn't already exist.
            if (!eventTable.ContainsKey(eventType))
            {
                eventTable.Add(eventType, null);
            }
            // Add the handler to the event.
            eventTable[eventType] = (Callback)eventTable[eventType] + handler;
        }
    }

    static public void RemoveListener(string eventType, Callback handler)
    {
        // Obtain a lock on the event table to keep this thread-safe.
        lock (eventTable)
        {
            // Only take action if this event type exists.
            if (eventTable.ContainsKey(eventType))
            {
                // Remove the event handler from this event.
                eventTable[eventType] = (Callback)eventTable[eventType] - handler;

                // If there's nothing left then remove the event type from the event table.
                if (eventTable[eventType] == null)
                {
                    eventTable.Remove(eventType);
                }
            }
        }
    }

    static public void Invoke(string eventType)
    {
        Delegate d;
        // Invoke the delegate only if the event type is in the dictionary.
        if (eventTable.TryGetValue(eventType, out d))
        {
            // Take a local copy to prevent a race condition if another thread
            // were to unsubscribe from this event.
            Callback callback = (Callback)d;

            // Invoke the delegate if it's not null.
            if (callback != null)
            {
                callback();
            }
        }
    }
	
	static public string DumpSinks()
	{
		string sb = "";
		int i = 0;
		
		sb = "\t\tMessenger class, 0 variables\n";
		foreach (string key in eventTable.Keys)
		{
			sb += "\t\t\t" + key;
			string methodName = eventTable[key].Method.ToString();
			string className = eventTable[key].Target.ToString();
			string[] temp = methodName.Split(' ');
			int length = 25;
			length -= key.Length;
			for (int j = 0; j < length; j++)
			{
				sb += " ";
			}
			sb += temp[0].ToLower() + " " + className + "." + temp[1] + "\n";
			i++;
		}
		sb += "\t\t\t\t" + i + " keys\n\n";
		
		return sb;
	}
} // end Messenger


/**
 * A messenger for events that have one parameter of type T.
 */
static public class Messenger<T>
{
    private static Dictionary<string, Delegate> eventTable = new Dictionary<string, Delegate>();

    static public void AddListener(string eventType, Callback<T> handler)
    {
        // Obtain a lock on the event table to keep this thread-safe.
        lock (eventTable)
        {
            // Create an entry for this event type if it doesn't already exist.
            if (!eventTable.ContainsKey(eventType))
            {
                eventTable.Add(eventType, null);
            }
            // Add the handler to the event.
            eventTable[eventType] = (Callback<T>)eventTable[eventType] + handler;
        }
    }

    static public void RemoveListener(string eventType, Callback<T> handler)
    {
        // Obtain a lock on the event table to keep this thread-safe.
        lock (eventTable)
        {
            // Only take action if this event type exists.
            if (eventTable.ContainsKey(eventType))
            {
                // Remove the event handler from this event.
                eventTable[eventType] = (Callback<T>)eventTable[eventType] - handler;

                // If there's nothing left then remove the event type from the event table.
                if (eventTable[eventType] == null)
                {
                    eventTable.Remove(eventType);
                }
            }
        }
    }

    static public void Invoke(string eventType, T arg1)
    {
        Delegate d;
        // Invoke the delegate only if the event type is in the dictionary.
        if (eventTable.TryGetValue(eventType, out d))
        {
            // Take a local copy to prevent a race condition if another thread
            // were to unsubscribe from this event.
            Callback<T> callback = (Callback<T>)d;

            // Invoke the delegate if it's not null.
            if (callback != null)
            {
                callback(arg1);
            }
        }
    }
	
	static public string DumpSinks()
	{
		string sb = "";
		int i = 0;
		
		sb = "\t\tMessenger class, 1 variable: " + typeof(T) + "\n";
		foreach (string key in eventTable.Keys)
		{
			sb += "\t\t\t" + key;
			string methodName = eventTable[key].Method.ToString();
			string className = eventTable[key].Target.ToString();
			string[] temp = methodName.Split(' ');
			int length = 25;
			length -= key.Length;
			for (int j = 0; j < length; j++)
			{
				sb += " ";
			}
			sb += temp[0].ToLower() + " " + className + "." + temp[1] + "\n";
			i++;
		}
		sb += "\t\t\t\t" + i + " keys\n\n";
		
		return sb;
	}
} // end Messenger<T>


/**
 * A messenger for events that have two parameters of types T and U.
 */
static public class Messenger<T, U>
{
    private static Dictionary<string, Delegate> eventTable = new Dictionary<string, Delegate>();

    static public void AddListener(string eventType, Callback<T, U> handler)
    {
        // Obtain a lock on the event table to keep this thread-safe.
        lock (eventTable)
        {
            // Create an entry for this event type if it doesn't already exist.
            if (!eventTable.ContainsKey(eventType))
            {
                eventTable.Add(eventType, null);
            }
            // Add the handler to the event.
            eventTable[eventType] = (Callback<T, U>)eventTable[eventType] + handler;
        }
    }

    static public void RemoveListener(string eventType, Callback<T, U> handler)
    {
        // Obtain a lock on the event table to keep this thread-safe.
        lock (eventTable)
        {
            // Only take action if this event type exists.
            if (eventTable.ContainsKey(eventType))
            {
                // Remove the event handler from this event.
                eventTable[eventType] = (Callback<T, U>)eventTable[eventType] - handler;

                // If there's nothing left then remove the event type from the event table.
                if (eventTable[eventType] == null)
                {
                    eventTable.Remove(eventType);
                }
            }
        }
    }

    static public void Invoke(string eventType, T arg1, U arg2)
    {
        Delegate d;
        // Invoke the delegate only if the event type is in the dictionary.
        if (eventTable.TryGetValue(eventType, out d))
        {
            // Take a local copy to prevent a race condition if another thread
            // were to unsubscribe from this event.
            Callback<T, U> callback = (Callback<T, U>)d;

            // Invoke the delegate if it's not null.
            if (callback != null)
            {
                callback(arg1, arg2);
            }
        }
    }
	
	static public string DumpSinks()
	{
		string sb = "";
		int i = 0;
		
		sb = "\t\tMessenger class, 2 variables: " + typeof(T) + ", " + typeof(U) + "\n";
		foreach (string key in eventTable.Keys)
		{
			sb += "\t\t\t" + key;
			string methodName = eventTable[key].Method.ToString();
			string className = eventTable[key].Target.ToString();
			string[] temp = methodName.Split(' ');
			int length = 25;
			length -= key.Length;
			for (int j = 0; j < length; j++)
			{
				sb += " ";
			}
			sb += temp[0].ToLower() + " " + className + "." + temp[1] + "\n";
			i++;
		}
		sb += "\t\t\t\t" + i + " keys\n\n";
		
		return sb;
	}
} // end Messenger<T, U>




/**
 * A messenger for events that have two parameters of types T, U, and V.
 */
static public class Messenger<T, U, V>
{
    private static Dictionary<string, Delegate> eventTable = new Dictionary<string, Delegate>();

    static public void AddListener(string eventType, Callback<T, U, V> handler)
    {
        // Obtain a lock on the event table to keep this thread-safe.
        lock (eventTable)
        {
            // Create an entry for this event type if it doesn't already exist.
            if (!eventTable.ContainsKey(eventType))
            {
                eventTable.Add(eventType, null);
            }
            // Add the handler to the event.
            eventTable[eventType] = (Callback<T, U, V>)eventTable[eventType] + handler;
        }
    }

    static public void RemoveListener(string eventType, Callback<T, U, V> handler)
    {
        // Obtain a lock on the event table to keep this thread-safe.
        lock (eventTable)
        {
            // Only take action if this event type exists.
            if (eventTable.ContainsKey(eventType))
            {
                // Remove the event handler from this event.
                eventTable[eventType] = (Callback<T, U, V>)eventTable[eventType] - handler;

                // If there's nothing left then remove the event type from the event table.
                if (eventTable[eventType] == null)
                {
                    eventTable.Remove(eventType);
                }
            }
        }
    }

    static public void Invoke(string eventType, T arg1, U arg2, V arg3)
    {
        Delegate d;
        // Invoke the delegate only if the event type is in the dictionary.
        if (eventTable.TryGetValue(eventType, out d))
        {
            // Take a local copy to prevent a race condition if another thread
            // were to unsubscribe from this event.
            Callback<T, U, V> callback = (Callback<T, U, V>)d;

            // Invoke the delegate if it's not null.
            if (callback != null)
            {
                callback(arg1, arg2, arg3);
            }
        }
    }
	
	static public string DumpSinks()
	{
		string sb = "";
		int i = 0;
		
		sb = "\t\tMessenger class, 3 variables: " + typeof(T) + ", " + typeof(U) + ", " + typeof(V) + "\n";
		foreach (string key in eventTable.Keys)
		{
			sb += "\t\t\t" + key;
			string methodName = eventTable[key].Method.ToString();
			string className = eventTable[key].Target.ToString();
			string[] temp = methodName.Split(' ');
			int length = 25;
			length -= key.Length;
			for (int j = 0; j < length; j++)
			{
				sb += " ";
			}
			sb += temp[0].ToLower() + " " + className + "." + temp[1] + "\n";
			i++;
		}
		sb += "\t\t\t\t" + i + " keys\n\n";
		
		return sb;
	}
} // end Messenger<T, U, V>



/**
 * A messenger for events that have two parameters of types T, U, V, and W.
 */
static public class Messenger<T, U, V, W>
{
    private static Dictionary<string, Delegate> eventTable = new Dictionary<string, Delegate>();

    static public void AddListener(string eventType, Callback<T, U, V, W> handler)
    {
        // Obtain a lock on the event table to keep this thread-safe.
        lock (eventTable)
        {
            // Create an entry for this event type if it doesn't already exist.
            if (!eventTable.ContainsKey(eventType))
            {
                eventTable.Add(eventType, null);
            }
            // Add the handler to the event.
            eventTable[eventType] = (Callback<T, U, V, W>)eventTable[eventType] + handler;
        }
    }

    static public void RemoveListener(string eventType, Callback<T, U, V, W> handler)
    {
        // Obtain a lock on the event table to keep this thread-safe.
        lock (eventTable)
        {
            // Only take action if this event type exists.
            if (eventTable.ContainsKey(eventType))
            {
                // Remove the event handler from this event.
                eventTable[eventType] = (Callback<T, U, V, W>)eventTable[eventType] - handler;

                // If there's nothing left then remove the event type from the event table.
                if (eventTable[eventType] == null)
                {
                    eventTable.Remove(eventType);
                }
            }
        }
    }

    static public void Invoke(string eventType, T arg1, U arg2, V arg3, W arg4)
    {
        Delegate d;
        // Invoke the delegate only if the event type is in the dictionary.
        if (eventTable.TryGetValue(eventType, out d))
        {
            // Take a local copy to prevent a race condition if another thread
            // were to unsubscribe from this event.
            Callback<T, U, V, W> callback = (Callback<T, U, V, W>)d;

            // Invoke the delegate if it's not null.
            if (callback != null)
            {
                callback(arg1, arg2, arg3, arg4);
            }
        }
    }
	
	static public string DumpSinks()
	{
		string sb = "";
		int i = 0;
		
		sb = "\t\tMessenger class, 4 variables: " + typeof(T) + ", " + typeof(U) + ", " + typeof(V) + ", " + typeof(W) + "\n";
		foreach (string key in eventTable.Keys)
		{
			sb += "\t\t\t" + key;
			string methodName = eventTable[key].Method.ToString();
			string className = eventTable[key].Target.ToString();
			string[] temp = methodName.Split(' ');
			int length = 25;
			length -= key.Length;
			for (int j = 0; j < length; j++)
			{
				sb += " ";
			}
			sb += temp[0].ToLower() + " " + className + "." + temp[1] + "\n";
			i++;
		}
		sb += "\t\t\t\t" + i + " keys\n\n";
		
		return sb;
	}
} // end Messenger<T, U, V, W>

static public class MessengerDump
{
	static public string DumpSinks()
	{
		string sb = "Messenger Sink Output\n";
		sb += Messenger.DumpSinks();
		sb += Messenger<string>.DumpSinks();
		sb += Messenger<int>.DumpSinks();
		sb += Messenger<string, string>.DumpSinks();
		sb += Messenger<string, int>.DumpSinks();
		
		return sb;
	}
}